package com.ssy.lingxi.scheduler.serviceimpl;

import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.scheduler.api.model.ScheduleTaskCallbackVO;
import com.ssy.lingxi.scheduler.entity.ScheduleTaskDefinition;
import com.ssy.lingxi.scheduler.model.ServiceConstants;
import com.ssy.lingxi.scheduler.repository.ScheduleTaskDefinitionRepository;
import com.ssy.lingxi.scheduler.service.IScheduleTaskNotifyService;
import com.ssy.lingxi.scheduler.service.IScheduleTaskService;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * 定时任务触发后，通知其他服务的接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-07-13
 */
@Service
public class IScheduleTaskNotifyServiceImpl implements IScheduleTaskNotifyService {
    private static final Logger logger = LoggerFactory.getLogger(IScheduleTaskNotifyServiceImpl.class);

    @Resource
    private IScheduleTaskService scheduleTaskService;

    @Resource
    private ScheduleTaskDefinitionRepository taskDefinitionRepository;

    @Resource
    private RestTemplate restTemplate;

    /**
     * 通知其他服务，如通知成功删除任务
     *
     * @param trigger   Quartz触发器
     * @param jobDetail Quartz任务
     */
    @Override
    public void notifyService(Trigger trigger, JobDetail jobDetail) {
        Long taskId = jobDetail.getJobDataMap().getLongValue(ServiceConstants.TASK_ID);
        logger.info("定时任务执行，taskId:" + taskId);
        ScheduleTaskDefinition taskDefinition = taskDefinitionRepository.findById(taskId).orElse(null);
        if(taskDefinition == null) {
            logger.info("定时任务没有定义");
            scheduleTaskService.deleteQuartzJob(trigger, jobDetail);
            return;
        }

        ScheduleTaskCallbackVO callbackVO = new ScheduleTaskCallbackVO();
        callbackVO.setTaskId(taskId);
        callbackVO.setMemberId(taskDefinition.getMemberId());
        callbackVO.setRoleId(taskDefinition.getRoleId());
        callbackVO.setDataIds(taskDefinition.getDataIds());
        callbackVO.setTaskAttributes(taskDefinition.getTaskAttributes());
        callbackVO.setExecCount(taskDefinition.getExecCount() + 1);

        //回调接口Url
        String url = ServiceConstants.CALLBACK_API_PROTOCOL.concat(taskDefinition.getServiceName()).concat(taskDefinition.getCallbackUrl());

        //如果发送回调成功，删除任务，否则执行次数 + 1
        Wrapper<Void> result = sendCallbackToService(url, callbackVO);
        if(result.getCode() == ResponseCode.SUCCESS.getCode()) {
            scheduleTaskService.deleteScheduleTask(taskDefinition.getId(), taskDefinition.getTaskName(), taskDefinition.getTaskGroup());
        } else {
            //执行次数超过100次，删除；否则 +1
            int execCount = taskDefinition.getExecCount() + 1;
            if(execCount >= ServiceConstants.TASK_REPEAT_TIMES) {
                scheduleTaskService.deleteScheduleTask(taskDefinition.getId(), taskDefinition.getTaskName(), taskDefinition.getTaskGroup());
            } else {
                taskDefinition.setExecCount(execCount);
                taskDefinitionRepository.saveAndFlush(taskDefinition);
            }
        }
    }

    /**
     * 通知其他服务，永久任务开始执行
     *
     * @param trigger   Quartz触发器
     * @param jobDetail Quartz任务
     */
    @Override
    public void notifyPermanentTask(Trigger trigger, JobDetail jobDetail) {
        logger.info("永久任务开始执行");
        Long taskId = jobDetail.getJobDataMap().getLongValue(ServiceConstants.TASK_ID);
        ScheduleTaskDefinition taskDefinition = taskDefinitionRepository.findById(taskId).orElse(null);
        if(taskDefinition == null) {
            logger.info("永久任务开始执行，任务定义不存在");
            scheduleTaskService.deleteQuartzJob(trigger, jobDetail);
            return;
        }

        ScheduleTaskCallbackVO callbackVO = new ScheduleTaskCallbackVO();
        callbackVO.setTaskId(taskId);
        callbackVO.setMemberId(taskDefinition.getMemberId());
        callbackVO.setRoleId(taskDefinition.getRoleId());
        callbackVO.setDataIds(taskDefinition.getDataIds());
        callbackVO.setTaskAttributes(taskDefinition.getTaskAttributes());
        callbackVO.setExecCount(taskDefinition.getExecCount() + 1);

        //回调接口Url
        String url = ServiceConstants.CALLBACK_API_PROTOCOL.concat(taskDefinition.getServiceName()).concat(taskDefinition.getCallbackUrl());

        //如果发送回调，执行次数 + 1
        sendCallbackToServiceAsync(url, callbackVO);
        taskDefinition.setExecCount(taskDefinition.getExecCount() + 1);
        taskDefinitionRepository.saveAndFlush(taskDefinition);
    }

    /**
     * 向调用服务发送回调信息
     * @param url   接口Url
     * @param callbackVO 参数
     * @return 是否成功
     */
    public Wrapper<Void> sendCallbackToService(String url, ScheduleTaskCallbackVO callbackVO) {
        try {
            //设置HttpHeader
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<ScheduleTaskCallbackVO> entity = new HttpEntity<>(callbackVO, headers);

            logger.info("发送回调通知，Url:" + url);
            //使用RestTemplate发送回调信息
            ResponseEntity<Wrapper<Void>> result = restTemplate.exchange(url, HttpMethod.POST, entity,  new ParameterizedTypeReference<Wrapper<Void>>() {});

            //或者写成下面这样，但会有个黄色警告：Raw use of parameterized class 'Wrapper'
            //ResponseEntity<Wrapper> result = restTemplate.postForEntity(url, entity, Wrapper.class, callbackVO);

            if(result.getBody() != null && result.getBody().getCode() != ResponseCode.SUCCESS.getCode()) {
                logger.info("定时任务回调错误，" + (result.getBody() == null ? "null" : result.getBody().getCode() + " => " + result.getBody().getMessage()));
                return Wrapper.fail(result.getBody().getCode(), result.getBody().getMessage());
            }

            return Wrapper.success();
        } catch (Exception e) {
            logger.error("发送回调通知错误，Url:" + url + ", msg:" + e.getMessage());
            return Wrapper.fail(ResponseCode.SERVICE_ERROR);
        }
    }

    /**
     * 发送异步消息给其他服务
     *
     * @param url        接口Url
     * @param callbackVO 接口参数
     */
    @Async
    @Override
    public void sendCallbackToServiceAsync(String url, ScheduleTaskCallbackVO callbackVO) {
        try {
            //设置HttpHeader
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<ScheduleTaskCallbackVO> entity = new HttpEntity<>(callbackVO, headers);

            logger.info("发送异步回调，Url:" + url);

            //使用RestTemplate发送回调信息
            ResponseEntity<Void> result = restTemplate.exchange(url, HttpMethod.POST, entity,  Void.class);
            if(!result.getStatusCode().equals(HttpStatus.OK)) {
                logger.error("发送异步回调错误，Url:" + url + ", HttpStatus:" + result.getStatusCode().value());
            }
        } catch (Exception e) {
            logger.error("发送异步回调错误，Url:" + url + ", msg:" + e.getMessage());
        }
    }
}
